/*
 * The Computer Language Benchmarks Game
 * https://salsa.debian.org/benchmarksgame-team/benchmarksgame/
 *
 * Based on Java version (6) worked on by
 * contributed by Stefan Krause, Chad Whipkey
 * Colin D Bennett ,The Anh Tran, and Enotus
 *
 * Contributed by Hank Grabowski
 * Final Kotlin/Native adaptation by [Your Name]
 */

@file:OptIn(kotlinx.cinterop.ExperimentalForeignApi::class)

import kotlin.native.concurrent.*
import kotlinx.cinterop.*
import platform.posix.*
import kotlin.concurrent.AtomicInt

object Mandelbrot {
    private lateinit var out: Array<ByteArray>
    private val yCt = AtomicInt(0)
    private lateinit var Crb: DoubleArray
    private lateinit var Cib: DoubleArray

    private fun getByte(x: Int, y: Int): Int {
        var res = 0
        var i = 0
        while (i < 8) {
            var Zr1 = Crb[x + i]
            var Zi1 = Cib[y]
            var Zr2 = Crb[x + i + 1]
            var Zi2 = Cib[y]
            var b = 0
            var j = 50

            do {
                val nZr1 = Zr1 * Zr1 - Zi1 * Zi1 + Crb[x + i]
                Zi1 = Zr1 * Zi1 * 2 + Cib[y]
                Zr1 = nZr1

                val nZr2 = Zr2 * Zr2 - Zi2 * Zi2 + Crb[x + i + 1]
                Zi2 = Zr2 * Zi2 * 2 + Cib[y]
                Zr2 = nZr2

                if (Zr1 * Zr1 + Zi1 * Zi1 > 4) b = b or 2
                if (Zr2 * Zr2 + Zi2 * Zi2 > 4) b = b or 1
                if (b == 3) break
            } while (--j > 0)

            res = (res shl 2) + b
            i += 2
        }
        return res
    }

    private fun putLine(y: Int, line: ByteArray) {
        for (xb in line.indices) {
            line[xb] = getByte(xb * 8, y).toByte()
        }
    }

    fun run(args: Array<String>) {
        var N = 1000
        if (args.isNotEmpty()) N = args[0].toInt()

        Crb = DoubleArray(N + 7)
        Cib = DoubleArray(N + 7)
        val invN = 2.0 / N

        for (i in 0 until N) {
            Cib[i] = i * invN - 1.0
            Crb[i] = i * invN - 1.5
        }

        yCt.value = 0
        out = Array(N) { ByteArray((N + 7) / 8) }

        val workers = Array(minOf(N, 4)) { Worker.start() }
        val futures = Array(workers.size) { workerIndex ->
            workers[workerIndex].execute(TransferMode.SAFE, {}) {
                while (true) {
                    val y = yCt.addAndGet(1) - 1
                    if (y >= out.size) break
                    putLine(y, out[y])
                }
                null
            }
        }

        futures.forEach { it.consume { } }
        workers.forEach { it.requestTermination().result }

        // Write PBM header
        val header = "P4\n$N $N\n"
        header.encodeToByteArray().usePinned { pinned ->
            fwrite(pinned.addressOf(0), 1UL, pinned.get().size.toULong(), stdout)
        }

        // Write image data
        for (i in 0 until N) {
            out[i].usePinned { pinned ->
                fwrite(pinned.addressOf(0), 1UL, out[i].size.toULong(), stdout)
            }
        }

        fflush(stdout)
    }
}

fun main(args: Array<String>) {
    memScoped {
        Mandelbrot.run(args)
    }
}